﻿// owner 蒋家伟

#pragma once
#include "ModelBase_home.h"

namespace jsdk
{
    class MODELBASE_EXPORT StringUtils
    {
    public:
#pragma region 宽窄字符串转换
        template<typename T>
        static std::wstring toWString(const T& src)
        {
            return std::to_wstring(src);
        }

        static std::wstring toUTF8WString(const std::string& str);

        // 模版特化
        static std::wstring toWString(const std::string& str);
        static std::wstring toWString(const char* pchar);
        static std::wstring toWString(const std::wstring& wstr);
        static std::wstring toWString(const wchar_t* pwchar);

        template<typename T>
        static std::string toString(const T& src)
        {
            return std::to_string(src);
        }
        // 模版特化
        static std::string toString(const std::wstring& wstr);
        static std::string toString(const std::string& str);
        static std::string toString(const wchar_t* pwchar);
        static std::string toString(const char* pchar);

        static std::string toUTF8String(const std::wstring& wstr);

        /// \ brief 16进制字符串转数字
        /// \ in data 16进制字符串, 需要保证长度为偶数, 其次需要保证字符串为16进制数
        template<typename RT, typename = std::enable_if_t<std::is_integral<RT>::value>>
        static RT HexStringToIntegral(const std::string& data)
        {
            RT value{};
            size_t size = data.size();
            // 如果不为偶数直接返回0
            if (size & 0x1)
                return value;
            size_t count = size / 2;
            std::vector<uint8_t> byteArray(count);
            for (size_t i = 0; i < count; i++)
            {
                uint8_t value = static_cast<uint8_t>(std::stoi(data.substr(i * 2, 2), 0, 16));
                byteArray[count - i - 1] = value;               // 采用反向写入是让memcpy时高低位不会出错，保证与输入的字符串值相同
            }

            if (sizeof(RT) < byteArray.size())
                return value;

            std::memcpy(&value, byteArray.data(), byteArray.size());
            return value;
        }
        
#pragma region
        /// \brief 静态字符串比较针对于常量字符串，编译期计算
        static constexpr bool isEqual(const char* pLeft, const char* pRight)
        {
            return (*pLeft =='\0' || *pRight =='\0') ? (*pLeft == *pRight) : ( *pLeft == *pRight && isEqual(pLeft + 1, pRight + 1) );
        }

        /// \brief 配合__FILE__宏判断，当前文件是否为头文件
        static constexpr bool isHeadFile(const char* pFileMacro)
        {
            return *(pFileMacro + 2) == '\0' ? ( (*(pFileMacro)=='.') && (*(pFileMacro+1)=='h') ) : isHeadFile(pFileMacro + 1);
        }

        /// \ brief 配合__FILE__宏判断，当前文件是否为cpp文件
        static constexpr bool isCppFile(const char* pFileMacro)
        {
            return *(pFileMacro + 4) == '\0' ? ( (*(pFileMacro) == '.') && (*(pFileMacro + 1) == 'c') && (*(pFileMacro + 2 ) == 'p') && (*(pFileMacro + 3) == 'p') ): isCppFile(pFileMacro + 1);
        }
    };
}

// 使用宏定义生成字符串
#define WSTRING_OF(T) (L""#T)
#define STRING_OF(T) (#T)

// 以下两个宏在使用的时候，即使定义在正确的文件中依然会出现"红色波浪线(表示语法错误)",事实上没有错误，编译可以成功。如果将CPP_FILE_CHECK声明在.h文件中则会编译失败，出现静态断言。
// 因此需要使用时不用过于在意红色波浪线
#define CPP_FILE_CHECK static_assert(jsdk::StringUtils::isCppFile(__FILE__), "Macro must be in .cpp file");
#define HEADER_FILE_CHECK static_assert(jsdk::StringUtils::isHeadFile(__FILE__), "Macro must be in .h file");
